/****************************************************************
 * Fulguro
 * Copyright (C) 2004 Christophe Clienti
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 ***************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <flgrCoreIO.h>
#include <flgrCoreDispatch.h>
#include <flgrCoreMalloc.h>
#include <flgrCoreVector.h>
#include <flgrCoreArithVector.h>
#include <flgrArith.h>
#include <flgrMeasureBase.h>
#include "flgrHistogram.h"


#define FLGR_MACRO_NORMALIZE1D(dtype)					\
  FLGR_Vector *min = flgr_vector_create(boundmin->spp,boundmin->type);	\
  FLGR_Vector *max = flgr_vector_create(boundmin->spp,boundmin->type);	\
  FLGR_Vector *tmp = flgr_vector_create(boundmin->spp,boundmin->type);	\
									\
  FLGR_DECLARE_FUNCTION;						\
									\
  flgr1d_measure_min_max_##dtype(datsrc,min,max);			\
									\
  flgr1d_arith_const_sub_##dtype(datdest,datsrc,min);			\
									\
  flgr_vector_sub(max,max,min);						\
  flgr_vector_sub(tmp,boundmax,boundmin);				\
  flgr_vector_div(max,tmp,max);						\
									\
  flgr1d_arith_const_mult_##dtype(datdest,datsrc,max);			\
									\
  flgr1d_arith_const_add_##dtype(datdest,datdest,boundmin);		\
									\
  flgr_vector_destroy(min);						\
  flgr_vector_destroy(max);						\
  flgr_vector_destroy(tmp);						\
									\
  return


void flgr1d_histogram_normalize_fgBIT(FLGR_Data1D *datdest, FLGR_Data1D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  flgr_no_define_type_function(FLGR_BIT);
}

void flgr1d_histogram_normalize_fgUINT8(FLGR_Data1D *datdest, FLGR_Data1D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE1D(fgUINT8);
}

void flgr1d_histogram_normalize_fgUINT16(FLGR_Data1D *datdest, FLGR_Data1D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE1D(fgUINT16);
}

void flgr1d_histogram_normalize_fgUINT32(FLGR_Data1D *datdest, FLGR_Data1D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE1D(fgUINT32);
}

void flgr1d_histogram_normalize_fgINT8(FLGR_Data1D *datdest, FLGR_Data1D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE1D(fgINT8);
}

void flgr1d_histogram_normalize_fgINT16(FLGR_Data1D *datdest, FLGR_Data1D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE1D(fgINT16);
}

void flgr1d_histogram_normalize_fgINT32(FLGR_Data1D *datdest, FLGR_Data1D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE1D(fgINT32);
}

void flgr1d_histogram_normalize_fgFLOAT32(FLGR_Data1D *datdest, FLGR_Data1D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE1D(fgFLOAT32);
}

void flgr1d_histogram_normalize_fgFLOAT64(FLGR_Data1D *datdest, FLGR_Data1D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE1D(fgFLOAT64);
}

FLGR_Ret flgr1d_histogram_normalize(FLGR_Data1D *datdest, FLGR_Data1D *datsrc, FLGR_Vector *boundmin, FLGR_Vector * boundmax) {
  FLGR_Ret ret;

  FLGR_DECLARE_FUNCTION;
  
  if((datdest==NULL) || (datsrc==NULL)){
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if((ret = flgr1d_is_data_same_attributes(datdest,datsrc,__FUNCTION__)) != FLGR_RET_OK) return ret;

  FLGR_DISPATCH_PROCEDURE(datdest->type, flgr1d_histogram_normalize, datdest, datsrc, boundmin, boundmax);

  return FLGR_RET_OK;
}



FLGR_Ret flgr1d_histogram_normalize_str(FLGR_Data1D *datdest, FLGR_Data1D *datsrc, char *boundmin, char *boundmax) {
  int ret;
  FLGR_Vector *vector_min;
  FLGR_Vector *vector_max;

  FLGR_DECLARE_FUNCTION;
  
  if((datsrc==NULL) || (datdest==NULL))  {
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  vector_min = flgr_vector_create(datsrc->spp,datsrc->type);
  vector_max = flgr_vector_create(datsrc->spp,datsrc->type);

  if((ret=flgr_vector_import_string(vector_min, boundmin)) != FLGR_RET_OK) {
    POST_ERROR("String is not valid ""%s""\n",boundmin);
    flgr_vector_destroy(vector_min);
    return ret;
  }

  if((ret=flgr_vector_import_string(vector_max, boundmax)) != FLGR_RET_OK) {
    POST_ERROR("String is not valid ""%s""\n",boundmax);
    flgr_vector_destroy(vector_max);
    return ret;
  }


  ret = flgr1d_histogram_normalize(datdest,datsrc,vector_min,vector_max);

  flgr_vector_destroy(vector_min);
  flgr_vector_destroy(vector_max);

  return ret;

}




















#define FLGR_MACRO_NORMALIZE2D(dtype)					\
  FLGR_Vector *min = flgr_vector_create(boundmin->spp,boundmin->type);	\
  FLGR_Vector *max = flgr_vector_create(boundmin->spp,boundmin->type);	\
  FLGR_Vector *tmp = flgr_vector_create(boundmin->spp,boundmin->type);	\
									\
  FLGR_DECLARE_FUNCTION;						\
									\
  flgr2d_measure_min_max_##dtype(datsrc,min,max);			\
  									\
  flgr2d_arith_const_sub_##dtype(datdest,datsrc,min);			\
									\
  flgr_vector_sub(max,max,min);						\
  flgr_vector_sub(tmp,boundmax,boundmin);				\
  flgr_vector_div(max,tmp,max);						\
									\
  flgr2d_arith_const_mult_##dtype(datdest,datsrc,max);			\
									\
  flgr2d_arith_const_add_##dtype(datdest,datdest,boundmin);		\
									\
  flgr_vector_destroy(min);						\
  flgr_vector_destroy(max);						\
  flgr_vector_destroy(tmp);						\
									\
  return


void flgr2d_histogram_normalize_fgBIT(FLGR_Data2D *datdest, FLGR_Data2D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  flgr_no_define_type_function(FLGR_BIT);
}

void flgr2d_histogram_normalize_fgUINT8(FLGR_Data2D *datdest, FLGR_Data2D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE2D(fgUINT8);
}

void flgr2d_histogram_normalize_fgUINT16(FLGR_Data2D *datdest, FLGR_Data2D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE2D(fgUINT16);
}

void flgr2d_histogram_normalize_fgUINT32(FLGR_Data2D *datdest, FLGR_Data2D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE2D(fgUINT32);
}

void flgr2d_histogram_normalize_fgINT8(FLGR_Data2D *datdest, FLGR_Data2D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE2D(fgINT8);
}

void flgr2d_histogram_normalize_fgINT16(FLGR_Data2D *datdest, FLGR_Data2D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE2D(fgINT16);
}

void flgr2d_histogram_normalize_fgINT32(FLGR_Data2D *datdest, FLGR_Data2D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE2D(fgINT32);
}

void flgr2d_histogram_normalize_fgFLOAT32(FLGR_Data2D *datdest, FLGR_Data2D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE2D(fgFLOAT32);
}

void flgr2d_histogram_normalize_fgFLOAT64(FLGR_Data2D *datdest, FLGR_Data2D *datsrc, FLGR_Vector *boundmin, FLGR_Vector *boundmax) {
  FLGR_MACRO_NORMALIZE2D(fgFLOAT64);
}

FLGR_Ret flgr2d_histogram_normalize(FLGR_Data2D *datdest, FLGR_Data2D *datsrc, FLGR_Vector *boundmin, FLGR_Vector * boundmax) {
  FLGR_Ret ret;

  FLGR_DECLARE_FUNCTION;
  
  if((datdest==NULL) || (datsrc==NULL)){
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if((ret = flgr2d_is_data_same_attributes(datdest,datsrc,__FUNCTION__)) != FLGR_RET_OK) return ret;

  FLGR_DISPATCH_PROCEDURE(datdest->type, flgr2d_histogram_normalize, datdest, datsrc, boundmin, boundmax);

  return FLGR_RET_OK;
}



FLGR_Ret flgr2d_histogram_normalize_str(FLGR_Data2D *datdest, FLGR_Data2D *datsrc, char *boundmin, char *boundmax) {
  int ret;
  FLGR_Vector *vector_min;
  FLGR_Vector *vector_max;

  FLGR_DECLARE_FUNCTION;
  
  if((datsrc==NULL) || (datdest==NULL))  {
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  vector_min = flgr_vector_create(datsrc->spp,datsrc->type);
  vector_max = flgr_vector_create(datsrc->spp,datsrc->type);

  if((ret=flgr_vector_import_string(vector_min, boundmin)) != FLGR_RET_OK) {
    POST_ERROR("String is not valid ""%s""\n",boundmin);
    flgr_vector_destroy(vector_min);
    return ret;
  }

  if((ret=flgr_vector_import_string(vector_max, boundmax)) != FLGR_RET_OK) {
    POST_ERROR("String is not valid ""%s""\n",boundmax);
    flgr_vector_destroy(vector_max);
    return ret;
  }


  ret = flgr2d_histogram_normalize(datdest,datsrc,vector_min,vector_max);

  flgr_vector_destroy(vector_min);
  flgr_vector_destroy(vector_max);


  return ret;

}




#define FLGR2D_MACRO_GET_HISTOGRAM(dtype,dtypeHist)			\
  dtypeHist *phisto = (dtypeHist *) histogram->array;			\
  dtypeHist tmp1;							\
  dtypeHist tmp2;							\
  dtype val;								\
  dtype *psrc;								\
  int i,j,k;								\
  int spp = datsrc->spp;						\
  int linelen = datsrc->size_x*spp;					\
									\
  flgr1d_clear_all(histogram);						\
									\
  for(i=0 ; i<datsrc->size_y ; i++) {					\
    psrc = (dtype*) datsrc->array[i];					\
									\
    for(k=0 ; k<spp ; k++) {						\
									\
      for(j=0 ; j<linelen ; j+=spp) {					\
 	val = flgr_get_array_##dtype(psrc, j+k);			\
	tmp1 = ((dtypeHist) (val))*spp+k;				\
	tmp2 = flgr_get_array_##dtypeHist(phisto,tmp1) + 1;		\
	flgr_set_array_##dtypeHist(phisto,tmp1,tmp2);			\
      }									\
 									\
   }									\
									\
  }									\
									\
  return


void flgr2d_get_histogram_fgBIT(FLGR_Data1D *histogram, FLGR_Data2D *datsrc) {
  flgr_no_define_type_function(FLGR_BIT);
}
void flgr2d_get_histogram_fgUINT8(FLGR_Data1D *histogram, FLGR_Data2D *datsrc) {
  if(histogram->type == FLGR_UINT32) {
    FLGR2D_MACRO_GET_HISTOGRAM(fgUINT8,fgUINT32);
  }else if(histogram->type == FLGR_UINT64) {
    FLGR2D_MACRO_GET_HISTOGRAM(fgUINT8,fgUINT64);    
  }else {
    POST_ERROR("Histogram type must be fgUINT32 or fgUINT64\n");
  }
}
void flgr2d_get_histogram_fgUINT16(FLGR_Data1D *histogram, FLGR_Data2D *datsrc) {
  if(histogram->type == FLGR_UINT32) {
    FLGR2D_MACRO_GET_HISTOGRAM(fgUINT16,fgUINT32);
  }else if(histogram->type == FLGR_UINT64) {
    FLGR2D_MACRO_GET_HISTOGRAM(fgUINT16,fgUINT64);    
  }else {
    POST_ERROR("Histogram type must be fgUINT32 or fgUINT64\n");
  }
}
void flgr2d_get_histogram_fgUINT32(FLGR_Data1D *histogram, FLGR_Data2D *datsrc) {
  flgr_no_define_type_function(FLGR_UINT32);
}
void flgr2d_get_histogram_fgINT8(FLGR_Data1D *histogram, FLGR_Data2D *datsrc) {
  flgr_no_define_type_function(FLGR_INT8);
}
void flgr2d_get_histogram_fgINT16(FLGR_Data1D *histogram, FLGR_Data2D *datsrc) {
  flgr_no_define_type_function(FLGR_INT16);
}
void flgr2d_get_histogram_fgINT32(FLGR_Data1D *histogram, FLGR_Data2D *datsrc) {
  flgr_no_define_type_function(FLGR_INT32);
}
void flgr2d_get_histogram_fgFLOAT32(FLGR_Data1D *histogram, FLGR_Data2D *datsrc) {
  flgr_no_define_type_function(FLGR_FLOAT32);
}
void flgr2d_get_histogram_fgFLOAT64(FLGR_Data1D *histogram, FLGR_Data2D *datsrc) {
  flgr_no_define_type_function(FLGR_FLOAT64);
}

FLGR_Ret flgr2d_get_histogram(FLGR_Data1D *histogram, FLGR_Data2D *datsrc) {
  int ret;

  FLGR_DECLARE_FUNCTION;
  
  if((datsrc==NULL) || (histogram==NULL))  {
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if(datsrc->spp != histogram->spp) {
    POST_ERROR("Histogram and image have different pixel vector sizes");
    return FLGR_RET_VECTOR_SIZE_DIFFERENT;
  }

  if(datsrc->type == FLGR_UINT8) {
    if(histogram->length<256) {
      POST_ERROR("histogram length must be at least 256 for UINT8 images\n");
      return FLGR_RET_SIZE_ERROR;
    }
  }else if(datsrc->type == FLGR_UINT16) {
    if(histogram->length<65536) {
      POST_ERROR("histogram length must be at least 65536 for UINT16 images\n");
      return FLGR_RET_SIZE_ERROR;
    }
  }

  FLGR_DISPATCH_PROCEDURE(datsrc->type, flgr2d_get_histogram, histogram,datsrc);

  return ret;

}






void flgr2d_get_otsu_threshold(FLGR_Vector *vecth, FLGR_Data2D *datsrc) {

}
